package com.deer.wms.ware.task.service.impl;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.deer.wms.base.system.model.*;
import com.deer.wms.base.system.service.ErpService;
import com.deer.wms.inventory.model.Inventory.Inventory;
import com.deer.wms.inventory.model.Inventory.InventoryVo;
import com.deer.wms.project.root.core.result.CommonCode;
import com.deer.wms.project.root.core.text.Convert;
import com.deer.wms.project.root.exception.ServiceException;
import com.deer.wms.project.root.util.DateUtils;
import com.deer.wms.project.root.util.HttpClient;
import com.deer.wms.project.root.util.RedisUtil;
import com.deer.wms.system.manage.model.DataDictDetail;
import com.deer.wms.ware.task.erp.UpdatePPF;
import com.deer.wms.ware.task.model.TimedOutLog;
import com.deer.wms.ware.task.service.ErpPushLogService;
import com.deer.wms.ware.task.service.PushErpService;
import com.deer.wms.ware.task.service.TimedOutLogService;
import io.lettuce.core.ConnectionFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import javax.activation.DataHandler;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
 * @Author: xu
 * @Date: 2022/05/28/17:51
 * @Description:
 */
@Service
@Slf4j
public class PushErpServiceImpl implements PushErpService {
    @Autowired
    private RedisUtil redisUtil;

    //    private static final String BaseUrl = "http://10.18.3.19:34398";  //测试账套
    @Value("${erp.baseUrl}")
    private String BaseUrl;   //正式
    @Value("${erp.tokenUrl}")
    private String tokenUrl;
    @Value("${erp.ClientID}")
    private String clientID;
    @Value("${erp.ClientSecret}")
    private String ClientSecret;
    private static final Map<String, String> urlMap = new HashMap<String, String>() {
        {
            put("pullAsn", "/api/Epichust/GetAllIMInSourceBills");//获取入库数据
            put("pushIMBIll", "/api/IMBill/PushIMBill");// 出入库回传
            put("create", "/api/QCBill/PushQCBill");//下推来料检验单，来料检验申请单
            put("createIMBill", "/api/IMBill/CreateIMBill");//手动建单子回传
            put("pullSo", "/api/Epichust/GetAllIMOutSourceBills");//出库数据源
            put("sDPackingDelivery", "/api/SDPackingDelivery/GetSDPackingDeliveriesByAgreementNo/");//出库数据源
            put("SDPacking", "/api/SDPacking/CreateSDPackingProcessTracking");
            put("CheckBill", "/api/Bill/CheckBill");
            put("exception", "/api/QCRawMaterialQualityAbnormal/CreateQCRawMaterialQualityAbnormalIfNotExist"); //移仓单
            put("special", "/api/QCRawMaterialConcessionAccept/CreateQCRawMaterialConcessionAcceptIfNotExist");//特采单
            put("inventory", "/api/Epichust/GetIMInventories/");//查询库存
            put("GetPOReceiving", "/api/Epichust/GetPOReceiving");//查询库存
            put("UpdatePPFeedAssignedQty", "/api/Epichust/UpdatePPFeedAssignedQty");//更新生产/委外加工投料单已分配数量
            put("PushPOReceivingReturn", "/api/POReceiving/PushPOReceivingReturn");//来料检验单，退料通知单

        }
    };


    @Autowired
    private ErpService erpService;
    @Autowired
    private ErpPushLogService erpPushLogService;

    /**
     * 下推创建新出入库单：
     * 72-收料通知单 ==> 1-外购入库单；
     * 702-来料检验申请单 ==> 1-外购入库单；
     * 702-来料检验申请单 ==> 5-委外加工产品入库单；
     * 85-生产任务单 ==> 2-产品入库单；
     * 88-生产投料单 ==> 24-生产领料单；
     * 88-委外投料单 ==> 28-委外加工出库单；
     * 83-发货通知单 ==> 41-调拨单；
     * 82-退货通知单 ==> 21-销售出库单；
     * 251300021-生产投料单 ==> 24-生产领料单；
     * 251300021-生产投料单 ==> 29-其他出库单；
     * 251300022-生产退料单 ==> 24-生产领料单；
     * 251300022-生产退料单 ==> 29-其他出库单；
     * 251300056-返修拆改通知单 ==> 10-其他入库单；
     * 251300056-返修拆改通知单 ==> 29-其他出库单；
     * 251300057-返修拆改投料单 ==> 29-其他出库单；
     * 251300058-返修拆改退料单 ==> 29-其他出库单；
     * 200000011-其他出库申请单 ==> 29-其他出库单；
     * 200000013-其他出库(红字退库)申请单 ==> 29-其他出库单
     *
     * @param bean
     */
    @Override
    public Map<String, String> pushIMBIll(OrderToErpBO bean) {

        Map<String, String> pushIMBIll = pushErpUrl(urlMap.get("pushIMBIll"), bean);
//        if (!pushIMBIll.get("code").equals("200")) {
//            throw new ServiceException(CommonCode.SERVER_INERNAL_ERROR, pushIMBIll.get("msg"));
//        }
        return pushIMBIll;
    }

    public List<InventoryVo> inventory(Map<String, Object> map) {

        String content = pushErpUrl(urlMap.get("inventory"), map).get("msg");
        return (List<InventoryVo>) JSON
                .parseArray(content, InventoryVo.class);

    }


    /**
     * 质检回传接口
     *
     * @param bean
     */
    @Override
    public String pushQCBIll(QcOrderToErpBO bean) {
        return pushErpUrl(urlMap.get("create"), bean).get("msg");
    }

    @Override
    public String PushPOReceivingReturn(QcOrderToErpBO bean) {
        return pushErpUrl(urlMap.get("PushPOReceivingReturn"), bean).get("msg");
    }


    /**
     * 获取入库数据源
     *
     * @param map@return
     */
    @Override
    public List<PurchaseOrderBO> GetAllIMInSourceBills(Map<String, Object> map) {
//        Map<String, String> map = new HashMap<>();
//        map.put("dateFrom", date);
        String content = pushErpUrl(urlMap.get("pullAsn"), map).get("msg");
        return (List<PurchaseOrderBO>) JSON
                .parseArray(content, PurchaseOrderBO.class);
    }

    /**
     * 出库数据源
     *
     * @param map
     * @return
     */
    @Override
    public List<BillOutBO> OutSourceBill(Map<String, Object> map) {

        String content = pushErpUrl(urlMap.get("pullSo"), map).get("msg");
        return (List<BillOutBO>) JSON
                .parseArray(content, BillOutBO.class);
    }

    /**
     * 创建新入库单
     */
    @Override
    public Map<String, String> CreateIMBill(OrderToErpBO bean) {

        Map<String, String> map = pushErpUrl(urlMap.get("createIMBill"), bean);
//        if (!map.get("code").equals("200")) {
//            throw new ServiceException(CommonCode.SERVER_INERNAL_ERROR, map.get("msg"));
//        }
        return map;
    }

    @Override
    public PackingDeliveryBo sDPackingDelivery(String agreementNo) {

        return JSON.parseArray(restFull(urlMap.get("sDPackingDelivery") + agreementNo), PackingDeliveryBo.class).get(0);

    }

    /**
     * 二次回传
     *
     * @param bean
     */
    @Override
    public Map<String, String> twiceSend(OrderToErpBO bean) {
        Map<String, String> pushIMBIll = pushErpUrl(urlMap.get("pushIMBIll"), bean);
        return pushIMBIll;
    }

    /**
     * 251300045-总箱件清单 数据回传
     *
     * @param sdPackingBo
     */
    @Override
    public Map<String, String> sDPacking(SDPackingBo sdPackingBo) {
        Map<String, String> sDPacking = pushErpUrl(urlMap.get("SDPacking"), sdPackingBo);
        if (!sDPacking.get("code").equals("200")) {
            throw new ServiceException(CommonCode.SERVER_INERNAL_ERROR, sDPacking.get("msg"));
        }
        return sDPacking;
    }

    /**
     * 质检审核
     *
     * @param map
     */
    @Override
    public String CheckBill(Map<String, Object> map) {

        Map<String, String> sDPacking = pushErpUrl(urlMap.get("CheckBill"), map);
//        if (!sDPacking.get("code").equals("200")) {
//            throw new ServiceException(CommonCode.SERVER_INERNAL_ERROR, sDPacking.get("msg"));
//        }
        return sDPacking.get("msg");
    }

    /**
     * 获取异常id
     *
     * @param abnormalBillNo
     * @return
     */
    @Override
    public Integer findException(String abnormalBillNo) {
        Map<String, Object> map = new HashMap<>();
        map.put("number", abnormalBillNo);
        Map<String, String> push = pushErpUrl(urlMap.get("exception"), map);
//        if (!push.get("code").equals("200")) {
//            throw new ServiceException(CommonCode.SERVER_INERNAL_ERROR, push.get("msg"));
//        }
        return Integer.valueOf(push.get("msg"));
    }

    /**
     * 获取特采单号id
     *
     * @param abnormalBillNo
     * @return
     */
    @Override
    public Integer findSpecial(String abnormalBillNo) {
        Map<String, Object> map = new HashMap<>();
        map.put("number", abnormalBillNo);
        Map<String, String> push = pushErpUrl(urlMap.get("special"), map);
//        if (!push.get("code").equals("200")) {
//            throw new ServiceException(CommonCode.SERVER_INERNAL_ERROR, push.get("msg"));
//        }
        return Integer.valueOf(push.get("msg"));
    }


    @Value("${wave.direct}")
    //直发虚仓
    private Integer directId;
    @Value("${wave.vmi}")
    //VMI仓库id
    private Integer vmiId;

    @Override


    public List<Integer> getNotAasnStock() {
        List<Integer> list = new ArrayList<>();
        list.add(directId);
        list.add(vmiId);
        return list;
    }

    /**
     * 获取入库数据源
     *
     * @param map
     * @return
     */
    @Override
    public PurchaseOrderBO erpByBillNo(Map<String, Object> map) {
        String content = pushErpUrl(urlMap.get("GetPOReceiving"), map).get("msg");
        System.out.println(content);

        return (PurchaseOrderBO) JSON
                .parseObject(content, PurchaseOrderBO.class);
    }

    /**
     * 冻结任务单数量
     *
     * @param list
     */
    @Override
    public void UpdatePPFeedAssignedQty(List<UpdatePPF> list) {
        log.info("回传参数{}", JSON.toJSONString(list));
        pushErpUrl(urlMap.get("UpdatePPFeedAssignedQty"), list).get("msg");
    }

    @Autowired
    private TimedOutLogService timedOutLogService;

    /**
     * @param url 统一回传接口
     * @param obj
     * @return
     */
    public Map<String, String> pushErpUrl(String url, Object obj) {

        //回传记录
        ErpBackRecord erpRecord = new ErpBackRecord();


        //开始时间
        LocalDateTime now = LocalDateTime.now();
        erpRecord.setParam(JSON.toJSONString(obj));
        erpRecord.setRequestTime(now);

        Map<String, String> repMap = new HashMap<>();
        try {
            /**
             * Post请求
             */
            HttpResponse execute = HttpRequest.post(BaseUrl + url)
                    .header(Header.CONTENT_TYPE, "application/json")//头信息，多个头信息多次调用此方法即可
                    .header(Header.AUTHORIZATION, "Bearer " + this.getToken())
                    .body(JSON.toJSONString(obj))//表单内容
//                    .timeout(60000)//超时，毫秒  60000=60秒
                    .timeout(60000)//超时，毫秒  60000=60秒
                    .execute();
            //状态码
            int status = execute.getStatus();
            //返回结果
            String body = execute.body();
            repMap.put("code", Convert.toStr(status));
            repMap.put("msg", body);

            Map<String, String> map = getType(url, body, obj);
            if (map.get("type") != null) {
                erpRecord.setType(Integer.parseInt(map.get("type")));
            }
            if (map.get("return") != null) {
                erpRecord.setMsg(map.get("return"));
            }
            /**
             //记录回传记录
             *
             */
            erpRecord.setCode(Convert.toStr(status));
            erpRecord.setMsg(body);
            erpRecord.setState(2);
            /**
             字段验证状态码不等于200 抛出异常
             *
             */
            if (!Validator.equal(200, status)) {
                erpRecord.setState(1);
                throw new ServiceException(CommonCode.SERVER_INERNAL_ERROR, body);
            }
            return repMap;

        } catch (Throwable throwable) {
            //删除token
            redisUtil.remove(ERP_TOKEN);
            /**
             * 请求超时记录请求超时日志表，异常catch事务不回滚
             */
            if (throwable instanceof IORuntimeException) {
                /**
                 * 业务逻辑
                 * Step0. 记录url,json,异常放过，后期手动提交
                 */
                TimedOutLog timedOutLog = new TimedOutLog();
                timedOutLog.setRequestUrl(url);
                timedOutLog.setRequestParams(JSON.toJSONString(obj));
                timedOutLog.setBeginTime(now);
                timedOutLog.setEndTime(LocalDateTime.now());
                timedOutLog.setDuration((int) LocalDateTimeUtil.between(timedOutLog.getBeginTime(), timedOutLog.getEndTime(), ChronoUnit.SECONDS));


                //异步记录日志
                CompletableFuture.runAsync(() -> {
                    try {
                        timedOutLogService.save(timedOutLog);
                    } catch (Exception e) {
                        log.info("回传ERP异常{}", timedOutLog.toString());
                    }
                });


                return repMap;
            }
            /**
             * 其他异常事务回滚
             */
            System.out.println(throwable.getMessage());
            throw new ServiceException(CommonCode.SERVER_INERNAL_ERROR, throwable.getMessage());
        } finally {
            try {
                if (ObjectUtil.isNotEmpty(erpRecord.getType())) {
                    erpRecord.setReturnTime(LocalDateTime.now());
                    erpService.saveErpBack(erpRecord);
                }
            } catch (Exception e) {
                log.info("回传ERP异常{}", erpRecord.toString());
            }

        }


    }

    private Map<String, String> getType(String url, String msg, Object obj) {
        Map<String, String> map = new HashMap<>();
        //  JSONObject json = (JSONObject) JSONObject.toJSON(entity);
        if (("/api/IMBill/PushIMBill").equals(url)) {//出入库回传
            OrderToErpBO bean = (OrderToErpBO) obj;
            String type = String.valueOf(bean.getBillTypeId());
            map.put("type", type);
            map.put("return", msg);
        } else if (( "/api/QCBill/PushQCBill").equals(url)) {//质检回传
            map.put("type", "999");
            map.put("return", msg);
        } else if (("/api/IMBill/CreateIMBill").equals(url)) {//手动创建单据回传
            map.put("type", "998");
            map.put("return", msg);
        } else if (("/api/Bill/CheckBill").equals(url)) {//审核回传
            map.put("type", "997");
            map.put("return", msg);
        } else if (("/api/Epichust/GetPOReceiving").equals(url)) {
            map.put("type", "996");
            map.put("return", msg);
        }
        return map;
    }

    /**
     * 解析回传字段
     *
     * @param is
     * @return
     * @throws IOException
     */
    public static String inputStream2String(InputStream is) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        String result = "";
        try {
            int len = 0;
            byte[] date = new byte[4];
            while ((len = is.read(date)) != -1) {
                //将每一次的数据写入缓冲区
                outputStream.write(date, 0, len);
            }
            byte[] bytes = outputStream.toByteArray();
            result = new String(bytes, "utf-8");
            is.close();
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;

    }

    final static String ERP_TOKEN = "erp_access_token";//正式
    //final static String ERP_TOKEN = "erp_access_token_test";//测试

    @Override
    public String getToken() {

        String erpToken = (String) redisUtil.get(ERP_TOKEN);

        if (ObjectUtil.isEmpty(erpToken)) {
            //请求token
            MultiValueMap<String, String> stringMultiValueMap = new LinkedMultiValueMap<>();
            stringMultiValueMap.add("client_id", this.clientID);
            stringMultiValueMap.add("client_secret", this.ClientSecret);
            stringMultiValueMap.add("grant_type", "password");
            stringMultiValueMap.add("username", "epichust");
            stringMultiValueMap.add("password", "hK@vmz0YI1e^thtB");
            String tokenUrl = this.tokenUrl + "/connect/token";
            String clientRg = null;
            try {
                clientRg = HttpClient.clientRg(tokenUrl, HttpMethod.POST, stringMultiValueMap);
            } catch (Exception e) {
                e.printStackTrace();
            }
            JSONObject jsonObject = JSONObject.parseObject(clientRg);
            erpToken = jsonObject.getString("access_token");
            //过期时间
            String pasts = jsonObject.getString("expires_in");
            redisUtil.set(ERP_TOKEN, erpToken, Long.valueOf(pasts));
        }
        return erpToken;
    }


    public String restFull(String url) {

        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Accept", "application/json");
        httpHeaders.add("Authorization", "Bearer " + getToken());
        String result = null;

        try {
            return HttpClient.getRequest(url, null, httpHeaders);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }


}
